package io.openmessaging.storage.dledger.protocol;

import io.openmessaging.storage.dledger.entry.DLedgerEntry;
import io.openmessaging.storage.dledger.utils.PreConditions;

import java.util.ArrayList;
import java.util.List;

public class PushEntryRequest extends RequestOrResponse {

    //
    private long commitIndex = -1;
    //
    private Type type = Type.APPEND;
    //
    private DLedgerEntry entry;

    //for batch append push
    private List<DLedgerEntry> batchEntry = new ArrayList<>();
    //
    private int totalSize;

    @Override
    public String toString() {
        return "PushEntryRequest{" +
                "commitIndex=" + commitIndex +
                ", type=" + type +
                ", entry=" + entry +
                ", batchEntry=" + batchEntry +
                ", totalSize=" + totalSize +
                '}';
    }

    public DLedgerEntry getEntry() {
        return entry;
    }

    public void setEntry(DLedgerEntry entry) {
        this.entry = entry;
    }

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public long getCommitIndex() {
        return commitIndex;
    }

    public void setCommitIndex(long commitIndex) {
        this.commitIndex = commitIndex;
    }

    public void addEntry(DLedgerEntry entry) {
        if (!batchEntry.isEmpty()) {
            PreConditions.check(batchEntry.get(0).getIndex() + batchEntry.size() == entry.getIndex(), DLedgerResponseCode.UNKNOWN, "batch push wrong order");
        }
        batchEntry.add(entry);
        totalSize += entry.getSize();
    }

    public long getFirstEntryIndex() {
        if (!batchEntry.isEmpty()) {
            return batchEntry.get(0).getIndex();
        } else if (entry != null) {
            return entry.getIndex();
        } else {
            return -1;
        }
    }

    public long getLastEntryIndex() {
        if (!batchEntry.isEmpty()) {
            return batchEntry.get(batchEntry.size() - 1).getIndex();
        } else if (entry != null) {
            return entry.getIndex();
        } else {
            return -1;
        }
    }

    public int getCount() {
        if (!batchEntry.isEmpty()) {
            return batchEntry.size();
        } else if (entry != null) {
            return 1;
        } else {
            return 0;
        }
    }

    public long getTotalSize() {
        return totalSize;
    }

    public List<DLedgerEntry> getBatchEntry() {
        return batchEntry;
    }

    public void clear() {
        batchEntry.clear();
        totalSize = 0;
    }

    public boolean isBatch() {
        return !batchEntry.isEmpty();
    }

    public enum Type {
        //将日志条目追加到从节点
        APPEND,

        //通常Leader节点会将提交的索引附加到append请求，
        //如果append请求很少且分散，Leader 节点将发送一个单独的请求来通知从节点提交索引
        COMMIT,

        //如果Leader节点发生变化，新的Leader节点需要与它的从节点日志条目进行比较，以便 截断 从节点多余的数据
        COMPARE,

        //如果Leader节点通过索引完成日志对比后，
        // 发现从节点存在多余的数据（未提交的数据），则Leader节点将发送
        //TRUNCATE给它的从节点，删除多余的数据，实现主从节点数据一致性
        TRUNCATE
    }
}
